Skip to content

fix: normalize faster-whisper CUDA device args#18

Merged
MapleEve merged 5 commits intomainfrom
fix/faster-whisper-device-index
Apr 29, 2026
Merged

fix: normalize faster-whisper CUDA device args#18
MapleEve merged 5 commits intomainfrom
fix/faster-whisper-device-index

Conversation

@MapleEve
Copy link
Copy Markdown
Owner

@MapleEve MapleEve commented Apr 29, 2026

Summary

  • normalize torch-style CUDA device strings before constructing faster-whisper WhisperModel
  • keep internal pipeline device state as cuda:N for torch/pyannote/embedding paths
  • load cached pyannote Hugging Face snapshots through pyannote-accepted local files instead of passing snapshot directories as repo ids
  • generate a runtime-localized diarization config for local snapshots so nested segmentation / embedding refs point at local pytorch_model.bin files
  • fail before pyannote loading when a required nested local model artifact is incomplete, with public-safe error text
  • keep Hub repo-id fallback behavior unchanged when no local diarization snapshot is selected
  • add regression coverage for faster-whisper CUDA device args, pyannote nested config localization, Hub passthrough, and missing nested-local-artifact errors
  • add v0.7.5 changelog fix notes in zh/en

Validation

  • RED: PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest tests/unit/test_pipeline_model_lifecycle.py -v --tb=short --no-header -> 3 failed, 3 passed before faster-whisper implementation
  • GREEN: PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 /tmp/voscript-faster-whisper-device-venv/bin/python -m pytest tests/unit/test_pipeline_model_lifecycle.py -v --tb=short --no-header -> 6 passed
  • RED: PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest tests/unit/test_huggingface_models.py -v --tb=short --no-header -> 3 failed, 11 passed before nested pyannote config localization
  • GREEN: PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest tests/unit/test_huggingface_models.py -v --tb=short --no-header -> 14 passed
  • Targeted orchestrator/HF/faster-whisper: PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest tests/unit/test_huggingface_models.py tests/unit/test_pipeline_model_lifecycle.py tests/unit/test_pipeline_package.py -v --tb=short --no-header -> 22 passed
  • Full slice: PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 /tmp/voscript-pr18-venv/bin/pytest tests/unit/ tests/test_security.py -v --tb=short --no-header -> 102 passed
  • ruff check app/ --ignore E501 -> passed
  • ruff format --check app/ -> passed
  • python public_release_scan.py --root <worktree> -> passed
  • git diff --check -> passed

Note: the system Python security slice had only the local FastAPI stub available, so the full pytest slice was rerun in a clean /tmp venv with real FastAPI/httpx/numpy test dependencies.

@github-actions
Copy link
Copy Markdown

👍 @MapleEve

Thank you for raising your pull request and contributing to voscript.
Please make sure you have followed our contributing guidelines. We will review it as soon as possible.
If you encounter any problems, please feel free to connect with us.
非常感谢您提出拉取请求并为 voscript 做出贡献,请确保您已经遵循了我们的贡献指南,我们会尽快审查它。
如果您遇到任何问题,请随时与我们联系。

@MapleEve MapleEve marked this pull request as ready for review April 29, 2026 05:16
Copilot AI review requested due to automatic review settings April 29, 2026 05:16
@MapleEve MapleEve merged commit b24b489 into main Apr 29, 2026
9 checks passed
@MapleEve MapleEve deleted the fix/faster-whisper-device-index branch April 29, 2026 05:17
@github-actions
Copy link
Copy Markdown

❤️ Great PR @MapleEve ❤️

The growth of project is inseparable from user feedback and contribution, thanks for your contribution!
项目的成长离不开用户反馈和贡献,感谢您的贡献!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves model-loading compatibility and offline/cache behavior in the transcription pipeline by normalizing faster-whisper CUDA device arguments and making pyannote local snapshot loading use the file paths pyannote expects (including runtime-localized nested model references).

Changes:

  • Normalize torch-style CUDA device strings (e.g., cuda:1) into faster-whisper-compatible kwargs (device="cuda", device_index=1).
  • Load pyannote diarization/embedding from cached local snapshots via expected local files, and generate a localized diarization config that points nested models at local weight files (with explicit failures for incomplete local artifacts).
  • Add unit tests + changelog notes; update FOSSA workflow to run scan on main pushes and a diff test on PRs.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
app/pipeline/orchestrator.py Implements faster-whisper device kwarg normalization and pyannote local-file resolution + config localization.
tests/unit/test_pipeline_model_lifecycle.py Adds regression coverage for faster-whisper CUDA device kwarg normalization.
tests/unit/test_huggingface_models.py Adds coverage for pyannote local snapshot file loading, nested-config localization, and safe failure modes.
doc/changelog.en.md Documents the bug fixes in the English changelog for v0.7.5.
doc/changelog.zh.md Documents the bug fixes in the Chinese changelog for v0.7.5.
.github/workflows/fossa.yml Runs FOSSA scan on main pushes and adds a PR-only diff test step.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

local_path = local_path / snapshot_filename

if not local_path.is_file():
raise FileNotFoundError(f"Local pyannote model file not found: {local_path}")
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raising FileNotFoundError here includes the full local filesystem path in the exception message. That string is later surfaced to clients via the job status error field (see run_transcription storing str(e)), so this leaks host paths. Consider raising a public-safe error message (or a custom exception) that does not embed local_path, while still logging the full path server-side for debugging.

Suggested change
raise FileNotFoundError(f"Local pyannote model file not found: {local_path}")
component = next(
(
component_name
for expected_filename, component_name in _LOCAL_PYANNOTE_CONFIG_MODELS.values()
if expected_filename == snapshot_filename
),
"pyannote",
)
logger.error("Local pyannote model file not found: %s", local_path)
raise LocalPyannoteModelArtifactError(
_public_safe_missing_pyannote_artifact(component)
)

Copilot uses AI. Check for mistakes.
)
cache_dir.mkdir(parents=True, exist_ok=True)
localized_config = cache_dir / "config.yaml"
localized_config.write_text(localized_content, encoding="utf-8")
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_localized_pyannote_config_path writes to a deterministic shared location under the system temp dir. In multi-worker deployments, two processes can race on write_text() and another reader may observe a partially written file. Consider writing to a temporary file and atomically renaming/replacing it (or using a file lock) to make this robust under concurrent startup/load.

Suggested change
localized_config.write_text(localized_content, encoding="utf-8")
temp_config_path: Path | None = None
try:
with tempfile.NamedTemporaryFile(
mode="w",
encoding="utf-8",
dir=cache_dir,
prefix="config.",
suffix=".tmp",
delete=False,
) as temp_config:
temp_config.write(localized_content)
temp_config.flush()
temp_config_path = Path(temp_config.name)
temp_config_path.replace(localized_config)
except Exception:
if temp_config_path is not None:
temp_config_path.unlink(missing_ok=True)
raise

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants